﻿namespace IntegrationTests.ValueConversionTests
{
    using System;
    using System.Linq;
    using DirectoryServicesProtocolsExtensions.LdapConnection;
    using DirectoryServicesProtocolsExtensions.SearchRequest;

    using Microsoft.VisualStudio.TestTools.UnitTesting;

    [TestClass]
    public class Int64EncodedTimeValueTests : SemanticValueConversionTest
    {
        [TestMethod]
        public void Int64EncodedTimeStampConversion()
        {
            // Get LastLogonTimeStamp via PowerShell
            const string dnToTestAgainst = "CN=Administrator,CN=Users,DC=fabrikam,DC=com";
            const string attributeToTestAgainst = "lastLogonTimeStamp";
            const string scriptText = "([datetime]::FromFileTime((Get-ADObject -identity " +
                                      "'" + dnToTestAgainst + "'" +
                                      " -properties " + attributeToTestAgainst +
                                      " | select -expandproperty " + attributeToTestAgainst + "))).ToUniversalTime()";
            var lastLogonTimeStampAquiredByPowerShell = (DateTime)ExecutePowerShellScript(scriptText)[0].BaseObject;
            Assert.IsNotNull(lastLogonTimeStampAquiredByPowerShell);

            // Get LastLogonTimeStamp via code under test
            var metaMapping = LdapConnection.GetAttributeMetaData(attributeToTestAgainst, LdapMetaMapFactory);
            var ldapQueryPropertiesContainer = new MockFabrikamActiveDirectoryQueryPropertiesContainer
            {
                SearchBaseDN = dnToTestAgainst,
                AttributesToLoad = new[] { attributeToTestAgainst }
            };

            var pagedSearchRequest = new PagedSearchRequest(ldapQueryPropertiesContainer);
            var codeResult = pagedSearchRequest.Execute(LdapConnection).Single()[0];

            var lastLogonTimeStampAquiredByCode =
                codeResult.Attributes[attributeToTestAgainst].GetValues(metaMapping.RawDotNetTypeEquivalent)[0];
            var lastLogonTimeStampSemanticallyConvertedByCode =
                metaMapping.SemanticValueConversionFunction(lastLogonTimeStampAquiredByCode);

            Assert.IsTrue(DateTime.Compare(
                TrimMilliseconds(lastLogonTimeStampAquiredByPowerShell),
                TrimMilliseconds((DateTime)lastLogonTimeStampSemanticallyConvertedByCode)) == 0);
        }

        [TestMethod]
        // User never logged-in lastlogon = 0
        public void Int64EncodedTimeStampConversionFor0Values()
        {
            // Get LastLogonTime via PowerShell, 
            // In AD lastlogon is stored as 0 if the user never logged-in
            // PowerShell returns Monday, January 01, 1601 12:00:00 AM in this case
            const string dnToTestAgainst = "CN=User1,OU=Test1001,DC=fabrikam,DC=com";
            const string attributeToTestAgainst = "lastLogon";
            const string scriptText = "([datetime]::FromFileTime((Get-ADObject -identity " +
                                      "'" + dnToTestAgainst + "'" +
                                      " -properties " + attributeToTestAgainst +
                                      " | select -expandproperty " + attributeToTestAgainst + "))).ToUniversalTime()";
            var startOfTimeForPowerShell = new DateTime(1601, 1, 1, 0, 0, 0);
            var lastLogonByPowerShell = (DateTime)ExecutePowerShellScript(scriptText)[0].BaseObject;
            Assert.IsNotNull(lastLogonByPowerShell);
            Assert.AreEqual(lastLogonByPowerShell, startOfTimeForPowerShell);

            // Get LastLogonTimeStamp via code under test
            var metaMapping = LdapConnection.GetAttributeMetaData(attributeToTestAgainst, LdapMetaMapFactory);
            var ldapQueryPropertiesContainer = new MockFabrikamActiveDirectoryQueryPropertiesContainer
            {
                SearchBaseDN = dnToTestAgainst,
                AttributesToLoad = new[] { attributeToTestAgainst }
            };

            var pagedSearchRequest = new PagedSearchRequest(ldapQueryPropertiesContainer);
            var codeResult = pagedSearchRequest.Execute(LdapConnection).Single()[0];

            var lastLogonByCode =
                codeResult.Attributes[attributeToTestAgainst].GetValues(metaMapping.RawDotNetTypeEquivalent)[0];
            var lastLogonTimeStampSemanticallyConvertedByCode =
                metaMapping.SemanticValueConversionFunction(lastLogonByCode);

            Assert.IsTrue(DateTime.Compare(
                TrimMilliseconds(lastLogonByPowerShell),
                TrimMilliseconds((DateTime)lastLogonTimeStampSemanticallyConvertedByCode)) == 0);
        }

        [TestMethod]
        // User never logged-in lastLogonTimeStamp is not set in AD (null)
        public void Int64EncodedTimeStampConversionForNullValues()
        {
            const string dnToTestAgainst = "CN=User1,OU=Test1001,DC=fabrikam,DC=com";
            const string attributeToTestAgainst = "lastLogonTimestamp";
            
            var ldapQueryPropertiesContainer = new MockFabrikamActiveDirectoryQueryPropertiesContainer
            {
                SearchBaseDN = dnToTestAgainst,
                AttributesToLoad = new[] { attributeToTestAgainst }
            };

            var pagedSearchRequest = new PagedSearchRequest(ldapQueryPropertiesContainer);
            var codeResult = pagedSearchRequest.Execute(LdapConnection).Single()[0];
            Assert.IsNotNull(codeResult);
            Assert.IsNotNull(codeResult.Attributes.Values);
            // If lastlogonTimeStamp is null in AD, the searchResult will not include the attribute in the Values collection
            Assert.IsFalse(codeResult.Attributes.Contains("lastLogonTimeStamp"));
        }

        [TestMethod]
        // User password is set not never expire
        public void Int64EncodedTimeStampConversionForInfinityInTheFutureValues()
        {
            // Get accountExpires via PowerShell, 
            // In AD accoiuntExpires is 9223372036854775807 if the account was never set an expiration date
            // The value is not convertable to datetime
            // Powershell will throw an exception trying to [datetime]::FromFileTime()
            const string dnToTestAgainst = "CN=User10,OU=Test1001,DC=fabrikam,DC=com";
            const string attributeToTestAgainst = "accountExpires";
            const string scriptText = "Get-ADObject -identity " +
                                      "'" + dnToTestAgainst + "'" +
                                      " -properties " + attributeToTestAgainst +
                                      " | select -expandproperty " + attributeToTestAgainst;
            const long neverForPowerShellAsInt64 = 9223372036854775807;
            var accountExpiresAquiredByPowerShell = ExecutePowerShellScript(scriptText)[0].BaseObject;
            Assert.IsNotNull(accountExpiresAquiredByPowerShell);
            Assert.AreEqual(accountExpiresAquiredByPowerShell, neverForPowerShellAsInt64);

            // Get LastLogonTimeStamp via code under test
            var metaMapping = LdapConnection.GetAttributeMetaData(attributeToTestAgainst, LdapMetaMapFactory);
            var ldapQueryPropertiesContainer = new MockFabrikamActiveDirectoryQueryPropertiesContainer
            {
                SearchBaseDN = dnToTestAgainst,
                AttributesToLoad = new[] { attributeToTestAgainst }
            };

            var pagedSearchRequest = new PagedSearchRequest(ldapQueryPropertiesContainer);
            var codeResult = pagedSearchRequest.Execute(LdapConnection).Single()[0];

            var accountExpiresAquiredByCode =
                codeResult.Attributes[attributeToTestAgainst].GetValues(metaMapping.RawDotNetTypeEquivalent)[0];
            var accountExpiresSemanticallyConvertedByCode =
                metaMapping.SemanticValueConversionFunction(accountExpiresAquiredByCode);

            Assert.IsTrue(DateTime.Compare(
                (DateTime)accountExpiresSemanticallyConvertedByCode, 
                DateTime.MaxValue) == 0);
        }

        [TestMethod]
        public void Int64EncodedTimeStampConversionForValuesCloseToInfinity()
        {
            //this user acount expires in {1/6/2113 12:00:00 AM}
            
            const string dnToTestAgainst = "CN=User11,OU=Test501,DC=fabrikam,DC=com";
            const string attributeToTestAgainst = "accountExpires";
            const string scriptText = "Get-ADObject -identity " +
                                      "'" + dnToTestAgainst + "'" +
                                      " -properties " + attributeToTestAgainst +
                                      " | select -expandproperty " + attributeToTestAgainst;
            var accountExpiresAquiredByPowerShell = ExecutePowerShellScript(scriptText)[0].BaseObject;
            Assert.IsNotNull(accountExpiresAquiredByPowerShell);
            
            var metaMapping = LdapConnection.GetAttributeMetaData(attributeToTestAgainst, LdapMetaMapFactory);
            var ldapQueryPropertiesContainer = new MockFabrikamActiveDirectoryQueryPropertiesContainer
            {
                SearchBaseDN = dnToTestAgainst,
                AttributesToLoad = new[] { attributeToTestAgainst }
            };

            var pagedSearchRequest = new PagedSearchRequest(ldapQueryPropertiesContainer);
            var codeResult = pagedSearchRequest.Execute(LdapConnection).Single()[0];

            var accountExpiresAquiredByCode =
                codeResult.Attributes[attributeToTestAgainst].GetValues(metaMapping.RawDotNetTypeEquivalent)[0];

            var accountExpiresSemanticallyConvertedByCode =
                metaMapping.SemanticValueConversionFunction(accountExpiresAquiredByCode);

            Assert.AreEqual(
                (Int64)accountExpiresAquiredByPowerShell,
                Int64.Parse((string)accountExpiresAquiredByCode));
        }
    }
}
